﻿using System;
using System.Collections.Generic;
using Microsoft.VisualStudio.Modeling;

namespace Project.Co_DataTransformationPrototype
{
    /// <summary>
    /// Class contains an extension method for the Dictionary class
    /// </summary>
    static class DictionaryExtensions
    {
        public static void AddElement(this IDictionary<Guid, ElementEventInfo> input, StateBase state, MappingBase sqlMapping)
        {
            if (typeof(Entity).Equals(state.EventContext.elementEventArgs.ModelElement.GetType()))
            {
                addEntityEvent(input, state, sqlMapping);
            }
            else if (typeof(EntityAttribute).Equals(state.EventContext.elementEventArgs.ModelElement.GetType()))
            {
                addAttributeEvent(input, state, sqlMapping);
            }
            else if (typeof(EntityAssociation).Equals(state.EventContext.elementEventArgs.ModelElement.GetType()))
            {
                addLinkEvent(input, state, sqlMapping);
            }
        }
       
        /// <summary>
        /// Helper method which adds entity events to the given eventstore (input)
        /// </summary>
        /// <param name="input"></param>
        /// <param name="state"></param>
        /// <param name="sqlMapping"></param>
        private static void addEntityEvent(IDictionary<Guid, ElementEventInfo> input, StateBase state, MappingBase sqlMapping)
        {
            ElementEventInfo value;
            EventContext entityContext = state.EventContext;
            Entity entity = state.EventContext.elementEventArgs.ModelElement as Entity;

            // Update SQL Mapping
            entityContext.sqlEventMapping = sqlMapping.handle(sqlMapping);

            entityContext.elementType = ContextElementType.Entity;

            if (input.TryGetValue(entity.Id, out value))
            {
                // Update existing eventInfo to reflect new changes
                value.entityEvent.Add(entityContext);
                
                if (value.entityState != null)
                    value.entityState = value.entityState.Handle(state);
                else
                    value.entityState = state;
                
                //if (value.sqlMapping == null)
                //    value. sqlMapping = sqlMapping.handle(sqlMapping);
                //else
                //    value.sqlMapping = value.sqlMapping.handle(sqlMapping);
                
                if (!string.IsNullOrEmpty(entity.CurrentName))
                    value.MainEntityName = entity.CurrentName;
                
                input[entity.Id] = value;
            }
            else
            {
                // Create eventInfo to reflect new changes
                value = new ElementEventInfo();
                value.entityEvent.Add(entityContext);
                value.entityState = state;
                //value.sqlMapping = sqlMapping.handle(sqlMapping);
                value.MainEntityName = entity.CurrentName;
                input.Add(entity.Id, value);
            }
        }

        /// <summary>
        /// Helper method which adds attribute events to the given eventstore (input)
        /// </summary>
        /// <param name="eventContext"></param>
        /// <param name="input"></param>
        private static void addAttributeEvent(IDictionary<Guid, ElementEventInfo> input, StateBase state, MappingBase sqlMapping)
        {
            Guid parentEntityID;
            ElementEventInfo value;
            StateBase attributeState;
            EventContext attributeContext = state.EventContext;
            EntityAttribute attribute = attributeContext.elementEventArgs.ModelElement as EntityAttribute;

            // Deleted attributes have no context, 
            // hence if an attribute is deleted its context can be found in my custom context "ParentEntity"
            if (attribute.IsDeleted)
                parentEntityID = attribute.ParentEntity.Id;
            else
                parentEntityID = attribute.Entity.Id;

            // Update SQL Mapping
            attributeContext.sqlEventMapping = sqlMapping.handle(sqlMapping);
            attributeContext.elementType = ContextElementType.Attribute;
            
            if (input.TryGetValue(parentEntityID, out value))
            {
                // Update attribute's eventContext
                value.attributeEvent.Add(attributeContext);

                // Update or create attribute's state
                if (value.entityAttributeState.TryGetValue(attribute.Id, out attributeState))
                    value.entityAttributeState[attribute.Id] = value.entityAttributeState[attribute.Id].Handle(state);
                else
                    value.entityAttributeState.Add(attribute.Id, state);

                //// Update attribute's mapping
                //if (value.sqlMapping == null)
                //    value.sqlMapping = sqlMapping.handle(sqlMapping);
                //else
                //    value.sqlMapping = value.sqlMapping.handle(sqlMapping);

                value.MainEntityName = attributeContext.entityName;

                // Add this updated eventInfo to event store
                input[parentEntityID] = value;
            }
            else
            {
                // Create attributeEvent to reflect new changes
                value = new ElementEventInfo();
                value.attributeEvent.Add(attributeContext);
                value.entityAttributeState.Add(attribute.Id, state);
                //value.sqlMapping = sqlMapping.handle(sqlMapping);
                value.MainEntityName = attributeContext.entityName;
                input.Add(parentEntityID, value);
            }
        }

        /// <summary>
        /// Helper method which adds link events to the given eventstore (input)
        /// </summary>
        /// <param name="input"></param>
        /// <param name="state"></param>
        /// <param name="sqlMapping"></param>
        private static void addLinkEvent(IDictionary<Guid, ElementEventInfo> input, StateBase state, MappingBase sqlMapping)
        {
            Guid parentEntityID;
            ElementEventInfo value;
            EventContext linkContext = state.EventContext;
            EntityAssociation link = linkContext.elementEventArgs.ModelElement as EntityAssociation;

            parentEntityID = link.Source.Id;

            // Update SQL Mapping
            linkContext.sqlEventMapping = sqlMapping.handle(sqlMapping);
            linkContext.elementType = ContextElementType.Link;

            if (input.TryGetValue(parentEntityID, out value))
            {
                // Update link's eventContext
                value.linkEvent.Add(linkContext);

                // Links don't have states (they either exist or they don't)

                //// Update link's mapping (foreign-key constraint mapping)
                //if (value.sqlMapping == null)
                //    value.sqlMapping = sqlMapping.handle(sqlMapping);
                //else
                //    value.sqlMapping = value.sqlMapping.handle(sqlMapping);

                value.MainEntityName = linkContext.entityName;

                // Add this updated eventInfo to event store
                input[parentEntityID] = value;
            }
            else
            {
                // Create linkEvent to reflect new changes
                value = new ElementEventInfo();
                value.linkEvent.Add(linkContext);
                //value.sqlMapping = sqlMapping.handle(sqlMapping);
                value.MainEntityName = linkContext.entityName;
                input.Add(parentEntityID, value);
            }
        }
    }
}
